// LevelSetViz.cpp : Defines the entry point for the application.
//
#include "Renderer.h"
#include "Application.h"
#include "LevelSet.h"
#include "Vector3T.h"

#include "LevelSetViz.h"

using namespace std;


// renderer initialized in main file
extern Renderer *g_renderer;

const float			BG_COLOR[] = {0, 0, 0, 1};


//
// @brief	Main entry
//
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
	// Init app config
	AppConfig config;
	config.appName = "LevelSetViz";
	config.cmdShow = nCmdShow;
	config.width = 1300;
	config.height = 800;
	config.hInstance = hInstance;
	
	// Init scene
	LevelSetViz viewer;
	config.scene = &viewer;

	// Start application
	return Application::GetInstance()->Process( config );
}


LevelSetViz::LevelSetViz()
: m_highlight_cell(0,0,0)
, m_fog_distance(20)
, m_show_level_set_grid(true)
{
}

LevelSetViz::~LevelSetViz()
{
}

bool VectorFieldRightCB( const uLocation& l, Vector3& value )
{
	value.set(0.1,0,0);
	return true;
}

bool VectorFieldLeftCB( const uLocation& l, Vector3& value )
{
	value.set(-0.1,0,0);
	return true;
}

// Handle keyboard and mouse input
bool LevelSetViz::HandleMessage( UINT msg, WPARAM wParam, LPARAM lParam )
{
	bool handled = true;

	static bool key_shift_pressed = false;
	static bool highlight_adjust_mode = false;
	static bool fog_adjust_mode = false;

	switch( msg )
	{
	case WM_KEYUP:
		switch( wParam )
		{
			case VK_SHIFT:
				key_shift_pressed = false;
				break;
			case 'H':
				highlight_adjust_mode = false;
				break;
			case 'F':
				fog_adjust_mode = false;
				break;
			default:
				handled = false;
				break;
		}
		break;
	case WM_KEYDOWN:
		switch( wParam )
		{	
			case VK_SPACE:
				m_camera.Reset();
				m_near_axis_pos.set( 0,0,0 );
				m_far_axis_pos.set( 0,0,1024 );
				break;
			case VK_SHIFT:
				key_shift_pressed = true;
				break;
			case VK_UP:
				if ( highlight_adjust_mode )
				{
					uDimension hc(0,0,0);
					key_shift_pressed ? hc.z = -1 : hc.y = 1;
					m_level_set.MoveHighlightCell( hc );
				}
				else if ( fog_adjust_mode )
				{
					m_fog_distance = clamp( m_fog_distance+1, 10, 30 );
				}
				// Grow or highlight cell
				else
				{
					m_level_set.Grow( 0.01f );
					m_level_set.RenderToMesh( m_mesh );
				}
				break;
			case VK_DOWN:
				if ( highlight_adjust_mode )
				{
					uDimension hc(0,0,0);
					key_shift_pressed ? hc.z = 1 : hc.y = -1;
					m_level_set.MoveHighlightCell( hc );
				}
				else if ( fog_adjust_mode )
				{
					m_fog_distance = clamp( m_fog_distance-1, 10, 30 );
				}
				// Shrink or highlight cell
				else
				{
					m_level_set.Grow( -0.01f );
					m_level_set.RenderToMesh( m_mesh );
				}
				break;
			case VK_RIGHT:
				if ( highlight_adjust_mode )
				{
					uDimension hc(1,0,0);
					m_level_set.MoveHighlightCell( hc );
				}
				// Apply vector field or highlight cell
				else 				
				{
					m_level_set.ApplyVectorField( &VectorFieldRightCB );
					m_level_set.RenderToMesh( m_mesh );
				}
				break;
			case VK_LEFT:
				if ( highlight_adjust_mode )
				{
					uDimension hc(-1,0,0);
					m_level_set.MoveHighlightCell( hc );
				}
				// Apply vector field or highlight cell
				else 				
				{
					m_level_set.ApplyVectorField( &VectorFieldLeftCB );
					m_level_set.RenderToMesh( m_mesh );
				}
				break;
			case 'W':
				m_mesh.m_render_mode = m_mesh.m_render_mode == Mesh::RENDER_MODE_DEFAULT ? Mesh::RENDER_MODE_WIREFRAME : Mesh::RENDER_MODE_DEFAULT;
				break;
			case 'H':
				highlight_adjust_mode = true;
				break;
			case 'F':
				fog_adjust_mode = true;
				break;
			case 'G':
				m_show_level_set_grid = !m_show_level_set_grid;
				break;
			case 'R':
				UnInit();
				Init();
				break;
			default:
				handled = false;
				break;
		}
		break;
	case WM_RBUTTONDOWN:
		{
			Vector2 spos = GetMousePos( lParam );
			Vector3 near_wpos, far_wpos;
			ScreenToWorld( spos, near_wpos, far_wpos );

			char str[1024];
			sprintf( str, "%.2f, %.2f", spos.x, spos.y );
			m_scene_texts.push_back( SceneText( str, spos.x/(m_screen_width/2.f), spos.y/(m_screen_height/2.f) ) );

			Vector2 spos1, spos2;
			bool v1 = WorldToScreen( near_wpos, spos1 );
			bool v2 = WorldToScreen( far_wpos, spos2 );
			m_near_axis_pos = near_wpos;
			m_far_axis_pos = far_wpos;
		}
		break;
	default:
		handled = false;
		break;
	};

	// Default key and mouse handler
	if ( !handled )
		return Scene::HandleMessage( msg, wParam, lParam );

	return true;
}

// Init the scene
bool LevelSetViz::Init()
{
	Scene::Init();
	
	int width = m_screen_width;
	int height = m_screen_height;

	// Setup render specific stuff
	{
		// Setup viewport and projection matrix
		glViewport( 0, 0, width, height );

		glMatrixMode( GL_PROJECTION );
		glLoadIdentity();
		m_camera.glPerspective( width, height );

		// Other OpenGL flags
		glEnable(GL_DEPTH_TEST);				// Depthtest
		glDepthFunc(GL_LESS);
		//glEnable(GL_CULL_FACE);					// Cull face
		//glEnable(GL_BLEND);						// Blending
		//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glShadeModel(GL_SMOOTH);				// Shading
	}

	// FOG
	{
		glEnable(GL_FOG);
		glFogi( GL_FOG_MODE, GL_LINEAR);
		glFogfv( GL_FOG_COLOR, BG_COLOR );
	}

	// Setup lighting
	{
		glEnable(GL_LIGHTING);

		//float lightPos[]		= { 0.f, 0.f, 5.f, 1.f };
		float lightPos[]		= { 0.f, 0.15f, 0.1f, 1.f };
		float lightAmbient[]	= { 0.2f, 0.2f, 0.2f, 1.f };
		float lightDiffuse[]	= { 0.7f, 0.7f, 0.7f, 1.f };
		float lightSpecular[]	= { 1, 1, 1, 1 };
		
		glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);
		glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);
		glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular);
		glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
		glEnable(GL_LIGHT0);


		//float lightPos2[]		= { -10.5f, 0.f, 0.f, 0.0f };
		//float lightAmbient2[]	= { 0.1f, 0.1f, 0.1f, 0.1f };
		//float lightDiffuse2[]	= { 0.1f, 0.1f, 0.1f, 0.1f };
		//glLightfv(GL_LIGHT1, GL_AMBIENT, lightAmbient2);
		//glLightfv(GL_LIGHT1, GL_DIFFUSE, lightDiffuse2);
		//glLightfv(GL_LIGHT1, GL_POSITION, lightPos2);
		//glEnable(GL_LIGHT1);

		// Setup material
		glEnable(GL_COLOR_MATERIAL);
		glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);

		// Set specular and shiniess
		float shininess = 15.0f;
		float specularColor[4] = {0.7, 0.7, 0.7, 1.f};
		glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess); // range 0 ~ 128
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specularColor);
	}


	// Hud Text
	ResetHUDText();


	/********************************************
				 Init objects here
	********************************************/

	m_level_set.SetSphere( 4, 2 );
	m_level_set.RenderToMesh( m_mesh );
	m_mesh.m_render_mode = Mesh::RENDER_MODE_WIREFRAME;

	return true;
}

void LevelSetViz::UnInit()
{
	/********************************************
				 UnInit objects here
	********************************************/

	Scene::UnInit();
}

void LevelSetViz::Update()
{
	Scene::Update();

	/********************************************
				 Update objects here
	********************************************/
	glFogf( GL_FOG_END, m_fog_distance );
}

void LevelSetViz::Render( Renderer* renderer )
{
	// Clear back buffer
	glClearColor( BG_COLOR[0], BG_COLOR[1], BG_COLOR[2], BG_COLOR[3] );
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;

	// Setup view matrix
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	m_camera.glLookAt();

	// HUD
	RenderHUD( renderer, 0.6f, 0.85f, m_HUD_text );

	// Scene texts
	glColor3f( 0.5f, 0.5f, 0.5f );
	RenderText( renderer, m_scene_texts );

	// Axis
	DrawAxis(m_near_axis_pos);
	DrawAxis(m_far_axis_pos);


	glEnable( GL_LIGHTING );


	/********************************************
				 Render objects here
	********************************************/
	glColor3f( 1.f, 1.f, 0 );
	m_mesh.Render();

	glDisable( GL_LIGHTING );

	if ( m_show_level_set_grid )
		m_level_set.RenderGrid( renderer, this );
}

// Reset hud text
void LevelSetViz::ResetHUDText()
{
	m_HUD_text.clear();

	m_HUD_text.push_back( "Keys" );
	m_HUD_text.push_back( "  SPACE : Reset Camera" );
	m_HUD_text.push_back( "  R     : Reset scene" );
	m_HUD_text.push_back( "  W     : Toggle wireframe" );
	m_HUD_text.push_back( "  G     : Show/Hide grid" );
	m_HUD_text.push_back( "" );
	m_HUD_text.push_back( "    U/D arrow : Grow/Shrink" );
	m_HUD_text.push_back( "    L/R arrow : Apply L/R vector field" );
	m_HUD_text.push_back( "" );
	m_HUD_text.push_back( "  Highlight Cell" );
	m_HUD_text.push_back( "    H + Arrows : Move L/R/U/D" );
	m_HUD_text.push_back( "    H + Shift+Arrows : Move F/B" );
	m_HUD_text.push_back( "" );
	m_HUD_text.push_back( "    F + U/D arrow : Fog distance" );
}